File Coverage

blib/lib/Net/CIDR.pm
Criterion Covered Total %
statement 270 481 56.1
branch 101 226 44.6
condition 31 138 22.4
subroutine 16 23 69.5
pod 8 8 100.0
total 426 876 48.6


line stmt bran cond sub pod time code
1             # Net::CIDR
2             #
3             # Copyright 2001-2025 Sam Varshavchik.
4             #
5             # with contributions from David Cantrell and brian d foy
6             #
7             # This program is free software; you can redistribute it
8             # and/or modify it under the same terms as Perl itself.
9              
10             package Net::CIDR;
11              
12             require 5.000;
13             #use strict;
14             #use warnings;
15              
16             require Exporter;
17             # use AutoLoader qw(AUTOLOAD);
18 1     1   249711 use Carp;
  1         1  
  1         5019  
19              
20             @ISA = qw(Exporter);
21              
22             # Items to export into callers namespace by default. Note: do not export
23             # names by default without a very good reason. Use EXPORT_OK instead.
24             # Do not simply export all your public functions/methods/constants.
25              
26             # This allows declaration use Net::CIDR ':all';
27             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
28             # will save memory.
29             %EXPORT_TAGS = ( 'all' => [ qw( range2cidr
30             cidr2range
31             cidr2octets
32             cidradd
33             cidrlookup
34             cidrvalidate
35             addr2cidr
36             addrandmask2cidr
37             ) ] );
38              
39             @EXPORT_OK = ( qw( range2cidr
40             cidr2range
41             cidr2octets
42             cidradd
43             cidrlookup
44             cidrvalidate
45             addr2cidr
46             addrandmask2cidr
47             ));
48              
49             @EXPORT = qw(
50              
51             );
52              
53             $VERSION = "0.27";
54              
55             1;
56              
57              
58             =pod
59              
60             =head1 NAME
61              
62             Net::CIDR - Manipulate IPv4/IPv6 netblocks in CIDR notation
63              
64             =head1 SYNOPSIS
65              
66             use Net::CIDR;
67              
68             use Net::CIDR ':all';
69              
70             my $var;
71              
72             if ($var = Net::CIDR::cidrvalidate($var))
73             {
74             // ... do something
75             }
76              
77             print join("\n",
78             Net::CIDR::range2cidr("192.168.0.0-192.168.255.255",
79             "10.0.0.0-10.3.255.255"))
80             . "\n";
81             #
82             # Output from above:
83             #
84             # 192.168.0.0/16
85             # 10.0.0.0/14
86              
87             print join("\n",
88             Net::CIDR::range2cidr(
89             "dead:beef::-dead:beef:ffff:ffff:ffff:ffff:ffff:ffff"))
90             . "\n";
91              
92             #
93             # Output from above:
94             #
95             # dead:beef::/32
96              
97             print join("\n",
98             Net::CIDR::range2cidr("192.168.1.0-192.168.2.255"))
99             . "\n";
100             #
101             # Output from above:
102             #
103             # 192.168.1.0/24
104             # 192.168.2.0/24
105              
106             print join("\n", Net::CIDR::cidr2range("192.168.0.0/16")) . "\n";
107             #
108             # Output from above:
109             #
110             # 192.168.0.0-192.168.255.255
111              
112             print join("\n", Net::CIDR::cidr2range("dead::beef::/46")) . "\n";
113             #
114             # Output from above:
115             #
116             # dead:beef::-dead:beef:3:ffff:ffff:ffff:ffff:ffff
117              
118             @list=("192.168.0.0/24");
119             @list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @list);
120              
121             print join("\n", @list) . "\n";
122             #
123             # Output from above:
124             #
125             # 192.168.0.0/23
126              
127             print join("\n", Net::CIDR::cidr2octets("192.168.0.0/22")) . "\n";
128             #
129             # Output from above:
130             #
131             # 192.168.0
132             # 192.168.1
133             # 192.168.2
134             # 192.168.3
135              
136             print join("\n", Net::CIDR::cidr2octets("dead::beef::/46")) . "\n";
137             #
138             # Output from above:
139             #
140             # dead:beef:0000
141             # dead:beef:0001
142             # dead:beef:0002
143             # dead:beef:0003
144              
145             @list=("192.168.0.0/24");
146             print Net::CIDR::cidrlookup("192.168.0.12", @list);
147             #
148             # Output from above:
149             #
150             # 1
151              
152             @list = Net::CIDR::addr2cidr("192.168.0.31");
153             print join("\n", @list);
154             #
155             # Output from above:
156             #
157             # 192.168.0.31/32
158             # 192.168.0.30/31
159             # 192.168.0.28/30
160             # 192.168.0.24/29
161             # 192.168.0.16/28
162             # 192.168.0.0/27
163             # 192.168.0.0/26
164             # 192.168.0.0/25
165             # 192.168.0.0/24
166             # 192.168.0.0/23
167             # [and so on]
168              
169             print Net::CIDR::addrandmask2cidr("195.149.50.61", "255.255.255.248")."\n";
170             #
171             # Output from above:
172             #
173             # 195.149.50.56/29
174              
175             =head1 DESCRIPTION
176              
177             The Net::CIDR package contains functions that manipulate lists of IP
178             netblocks expressed in CIDR notation.
179             The Net::CIDR functions handle both IPv4 and IPv6 addresses.
180              
181             The cidrvalidate() function, described below, checks that its argument
182             is a single, valid IP address or a CIDR. The remaining functions
183             expect that
184             their parameters consist of validated IPs or CIDRs. See cidrvalidate()
185             and BUGS, below, for more information.
186              
187             =head2 @cidr_list=Net::CIDR::range2cidr(@range_list);
188              
189             Each element in the @range_list is a string "start-finish", where
190             "start" is the first IP address and "finish" is the last IP address.
191             range2cidr() converts each range into an equivalent CIDR netblock.
192             It returns a list of netblocks except in the case where it is given
193             only one parameter and is called in scalar context.
194              
195             For example:
196              
197             @a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
198              
199             The result is a one-element array, with $a[0] being "192.168.0.0/16".
200             range2cidr() processes each "start-finish" element in @range_list separately.
201             But if invoked like so:
202              
203             $a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255");
204              
205             The result is a scalar "192.168.0.0/16".
206              
207             Where each element cannot be expressed as a single CIDR netblock
208             range2cidr() will generate as many CIDR netblocks as are necessary to cover
209             the full range of IP addresses. Example:
210              
211             @a=Net::CIDR::range2cidr("192.168.1.0-192.168.2.255");
212              
213             The result is a two element array: ("192.168.1.0/24","192.168.2.0/24");
214              
215             @a=Net::CIDR::range2cidr(
216             "d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff");
217              
218             The result is an one element array: ("d08c:43::/32") that reflects this
219             IPv6 netblock in CIDR notation.
220              
221             range2cidr() does not merge adjacent or overlapping netblocks in
222             @range_list.
223              
224             =head2 @range_list=Net::CIDR::cidr2range(@cidr_list);
225              
226             The cidr2range() functions converts a netblock list in CIDR notation
227             to a list of "start-finish" IP address ranges:
228              
229             @a=Net::CIDR::cidr2range("10.0.0.0/14", "192.168.0.0/24");
230              
231             The result is a two-element array:
232             ("10.0.0.0-10.3.255.255", "192.168.0.0-192.168.0.255").
233              
234             @a=Net::CIDR::cidr2range("d08c:43::/32");
235              
236             The result is a one-element array:
237             ("d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff").
238              
239             cidr2range() does not merge adjacent or overlapping netblocks in
240             @cidr_list.
241              
242             =head2 @netblock_list = Net::CIDR::addr2cidr($address);
243              
244             The addr2cidr function takes an IP address and returns a list of all
245             the CIDR netblocks it might belong to:
246              
247             @a=Net::CIDR::addr2cidr('192.168.0.31');
248              
249             The result is a thirtythree-element array:
250             ('192.168.0.31/32', '192.168.0.30/31', '192.168.0.28/30', '192.168.0.24/29',
251             [and so on])
252             consisting of all the possible subnets containing this address from
253             0.0.0.0/0 to address/32.
254              
255             Any addresses supplied to addr2cidr after the first will be ignored.
256             It works similarly for IPv6 addresses, returning a list of one hundred
257             and twenty nine elements.
258              
259             =head2 $cidr=Net::CIDR::addrandmask2cidr($address, $netmask);
260              
261             The addrandmask2cidr function takes an IP address and a netmask, and
262             returns the CIDR range whose size fits the netmask and which contains
263             the address. It is an error to supply one parameter in IPv4-ish
264             format and the other in IPv6-ish format, and it is an error to supply
265             a netmask which does not consist solely of 1 bits followed by 0 bits.
266             For example, '255.255.248.192' is an invalid netmask, as is
267             '255.255.255.32' because both contain 0 bits in between 1 bits.
268              
269             Technically speaking both of those *are* valid netmasks, but a) you'd
270             have to be insane to use them, and b) there's no corresponding CIDR
271             range.
272              
273             =cut
274              
275             # CIDR to start-finish
276              
277             sub cidr2range {
278 12     12 1 15587 my @cidr=@_;
279              
280 12         18 my @r;
281              
282 12         35 while ($#cidr >= 0)
283             {
284 32         53 my $cidr=shift @cidr;
285              
286 32         65 $cidr =~ s/\s//g;
287              
288 32 100       139 unless ($cidr =~ /(.*)\/(.*)/)
289             {
290 10         15 push @r, $cidr;
291 10         24 next;
292             }
293              
294 22         79 my ($ip, $pfix)=($1, $2);
295              
296 22         31 my $isipv6;
297              
298 22         48 my @ips=_iptoipa($ip);
299              
300 22         39 $isipv6=shift @ips;
301              
302 22 50 33     164 croak "$pfix, as in '$cidr', does not make sense"
      33        
303             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
304              
305 22         53 my @rr=_cidr2iprange($pfix, @ips);
306              
307 22         55 while ($#rr >= 0)
308             {
309 22         46 my $a=shift @rr;
310 22         36 my $b=shift @rr;
311              
312 22         53 $a =~ s/\.$//;
313 22         33 $b =~ s/\.$//;
314              
315 22 100       47 if ($isipv6)
316             {
317 11         24 $a=_ipv4to6($a);
318 11         54 $b=_ipv4to6($b);
319             }
320              
321 22         128 push @r, "$a-$b";
322             }
323             }
324              
325 12         36 return @r;
326             }
327              
328             #
329             # If the input is an IPv6-formatted address, convert it to an IPv4 decimal
330             # format, since the other functions know how to deal with it. The hexadecimal
331             # IPv6 address is represented in dotted-decimal form, like IPv4.
332             #
333              
334             sub _ipv6to4 {
335 92     92   130 my $ipv6=shift;
336              
337 92 100       281 return (undef, $ipv6) unless $ipv6 =~ /:/;
338              
339 47 50       181 croak "Syntax error: $ipv6"
340             unless $ipv6 =~ /^[a-fA-F0-9:\.]+$/;
341              
342 47         68 my $ip4_suffix="";
343              
344 47 50       208 ($ipv6, $ip4_suffix)=($1, $2)
345             if $ipv6 =~ /^(.*:)([0-9]+\.[0-9\.]+)$/;
346              
347 47         180 $ipv6 =~ s/([a-fA-F0-9]+)/_h62d($1)/ge;
  194         330  
348              
349 47         96 my $ipv6_suffix="";
350              
351 47 100       170 if ($ipv6 =~ /(.*)::(.*)/)
352             {
353 42         106 ($ipv6, $ipv6_suffix)=($1, $2);
354 42         58 $ipv6_suffix .= ".$ip4_suffix";
355             }
356             else
357             {
358 5         12 $ipv6 .= ".$ip4_suffix";
359             }
360              
361 47         425 my @p=grep (/./, split (/[^0-9]+/, $ipv6));
362              
363 47         218 my @s=grep (/./, split (/[^0-9]+/, $ipv6_suffix));
364              
365 47         373 push @p, 0 while $#p + $#s < 14;
366              
367 47         226 my $n=join(".", @p, @s);
368              
369             # return (undef, $1)
370             # if $n =~ /^0\.0\.0\.0\.0\.0\.0\.0\.0\.0\.255\.255\.(.*)$/;
371              
372 47         226 return (1, $n);
373             }
374              
375             # Let's go the other way around
376              
377             sub _ipv4to6 {
378 1441     1441   15968 my @octets=split(/[^0-9]+/, shift);
379              
380 1441 50       3589 croak "Internal error in _ipv4to6"
381             unless $#octets == 15;
382              
383 1441         7439 my @dummy=@octets;
384              
385 1441 50       5194 return ("::ffff:" . join(".", $octets[12], $octets[13], $octets[14], $octets[15]))
386             if join(".", splice(@dummy, 0, 12)) eq "0.0.0.0.0.0.0.0.0.0.255.255";
387              
388 1441         3785 my @words;
389              
390             my $i;
391              
392 1441         3247 for ($i=0; $i < 8; $i++)
393             {
394 11528         35550 $words[$i]=sprintf("%x", $octets[$i*2] * 256 + $octets[$i*2+1]);
395             }
396              
397 1441         2089 my $ind= -1;
398 1441         2218 my $indlen= -1;
399              
400 1441         2863 for ($i=0; $i < 8; $i++)
401             {
402 5251 100       12396 next unless $words[$i] eq "0";
403              
404 1741         2342 my $j;
405              
406 1741         3763 for ($j=$i; $j < 8; $j++)
407             {
408 8458 100       20193 last if $words[$j] ne "0";
409             }
410              
411 1741 100       3597 if ($j - $i > $indlen)
412             {
413 1623         2650 $indlen= $j-$i;
414 1623         2393 $ind=$i;
415 1623         3617 $i=$j-1;
416             }
417             }
418              
419 1441 100       3200 return "::" if $indlen == 8;
420              
421 1374 100       2851 return join(":", @words) if $ind < 0;
422              
423 1357         2611 my @s=splice (@words, $ind+$indlen);
424              
425 1357         11805 return join(":", splice (@words, 0, $ind)) . "::"
426             . join(":", @s);
427             }
428              
429             # An IP address to an octet list.
430              
431             # Returns a list. First element, flag: true if it was an IPv6 flag. Remaining
432             # values are octets.
433              
434             sub _iptoipa {
435 44     44   71 my $iparg=shift;
436              
437 44         70 my $isipv6;
438             my $ip;
439              
440 44         104 ($isipv6, $ip)=_ipv6to4($iparg);
441              
442 44         411 my @ips= split (/\.+/, $ip);
443              
444             grep {
445 44 50 33     91 croak "$_, in $iparg, is not a byte" unless $_ >= 0 && $_ <= 255 && $_ =~ /^[0-9]+$/;
  452   33     2294  
446             } @ips;
447              
448 44         196 return ($isipv6, @ips);
449             }
450              
451             sub _h62d {
452 194     194   303 my $h=shift;
453              
454 194         292 $h=hex("0x$h");
455              
456 194         680 return ( int($h / 256) . "." . ($h % 256));
457             }
458              
459             sub _cidr2iprange {
460 139     139   375 my @ips=@_;
461 139         227 my $pfix=shift @ips;
462              
463 139 100       231 if ($pfix == 0)
464             {
465 21         28 grep { $_=0 } @ips;
  92         145  
466              
467 21         66 my @ips2=@ips;
468              
469 21         29 grep { $_=255 } @ips2;
  92         132  
470              
471 21         181 return ( join(".", @ips), join(".", @ips2));
472             }
473              
474 118 100       188 if ($pfix >= 8)
475             {
476 117         147 my $octet=shift @ips;
477              
478 117         264 @ips=_cidr2iprange($pfix - 8, @ips);
479              
480 117         204 grep { $_="$octet.$_"; } @ips;
  234         383  
481 117         295 return @ips;
482             }
483              
484 1         3 my $octet=shift @ips;
485              
486 1         3 grep { $_=0 } @ips;
  10         19  
487              
488 1         5 my @ips2=@ips;
489              
490 1         2 grep { $_=255 } @ips2;
  10         18  
491              
492 1         6 my @r= _cidr2range8(($octet, $pfix));
493              
494 1         8 $r[0] = join (".", ($r[0], @ips));
495 1         7 $r[1] = join (".", ($r[1], @ips2));
496              
497 1         7 return @r;
498             }
499              
500             #
501             # ADDRESS to list of CIDR netblocks
502             #
503              
504             sub addr2cidr {
505 19     19 1 9744 my @ips=_iptoipa(shift);
506              
507 19         59 my $isipv6=shift @ips;
508              
509 19         36 my $nbits;
510              
511 19 100       62 if ($isipv6)
512             {
513 11 50       30 croak "An IPv6 address is 16 bytes long" unless $#ips == 15;
514 11         23 $nbits=128;
515             }
516             else
517             {
518 8 50       19 croak "An IPv4 address is 4 bytes long" unless $#ips == 3;
519 8         14 $nbits=32;
520             }
521              
522 19         31 my @blocks;
523              
524 19         119 foreach my $bits (reverse 0..$nbits)
525             {
526 1683         6313 my @ipcpy=@ips;
527              
528 1683         2634 my $n=$bits;
529              
530 1683         3512 while ($n < $nbits)
531             {
532 12608         24144 @ipcpy[$n / 8] &= (0xFF00 >> ($n % 8));
533              
534 12608         17707 $n += 8;
535              
536 12608         23796 $n &= 0xF8;
537             }
538              
539 1683         3150 my $s=join(".", map { s/\A0+([0-9])/$1/; $_ } @ipcpy);
  23760         36116  
  23760         43589  
540              
541 1683 100       6587 push @blocks, ($isipv6 ? _ipv4to6($s):$s) . "/$bits";
542             }
543 19         591 return @blocks;
544             }
545              
546             # Address and netmask to CIDR
547              
548             sub addrandmask2cidr {
549 0     0 1 0 my $address = shift;
550 0         0 my($a_isIPv6) = _ipv6to4($address);
551 0         0 my($n_isIPv6, $netmask) = _ipv6to4(shift);
552 0 0 0     0 die("Both address and netmask must be the same type")
      0        
553             if( defined($a_isIPv6) && defined($n_isIPv6) && $a_isIPv6 != $n_isIPv6);
554 0         0 my $bitsInNetmask = 0;
555 0         0 my $previousNMoctet = 255;
556 0         0 foreach my $octet (split/\./, $netmask) {
557 0 0 0     0 die("Invalid netmask") if($previousNMoctet != 255 && $octet != 0);
558 0         0 $previousNMoctet = $octet;
559 0 0       0 $bitsInNetmask +=
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
560             ($octet == 255) ? 8 :
561             ($octet == 254) ? 7 :
562             ($octet == 252) ? 6 :
563             ($octet == 248) ? 5 :
564             ($octet == 240) ? 4 :
565             ($octet == 224) ? 3 :
566             ($octet == 192) ? 2 :
567             ($octet == 128) ? 1 :
568             ($octet == 0) ? 0 :
569             die("Invalid netmask");
570             }
571 0         0 return (grep { /\/$bitsInNetmask$/ } addr2cidr($address))[0];
  0         0  
572             }
573              
574             #
575             # START-FINISH to CIDR list
576             #
577              
578             sub range2cidr {
579 0     0 1 0 my @r=@_;
580              
581 0         0 my $i;
582              
583             my @c;
584              
585 0         0 for ($i=0; $i <= $#r; $i++)
586             {
587 0         0 $r[$i] =~ s/\s//g;
588              
589 0 0       0 if ($r[$i] =~ /\//)
590             {
591 0         0 push @c, $r[$i];
592 0         0 next;
593             }
594              
595 0 0       0 $r[$i]="$r[$i]-$r[$i]" unless $r[$i] =~ /(.*)-(.*)/;
596              
597 0         0 $r[$i] =~ /(.*)-(.*)/;
598              
599 0         0 my ($a,$b)=($1,$2);
600              
601 0         0 my $isipv6_1;
602             my $isipv6_2;
603              
604 0         0 ($isipv6_1, $a)=_ipv6to4($a);
605 0         0 ($isipv6_2, $b)=_ipv6to4($b);
606              
607 0 0 0     0 if ($isipv6_1 || $isipv6_2)
608             {
609 0 0 0     0 croak "Invalid netblock range: $r[$i]"
610             unless $isipv6_1 && $isipv6_2;
611             }
612              
613 0         0 my @a=split(/\.+/, $a);
614 0         0 my @b=split(/\.+/, $b);
615              
616 0 0       0 croak unless $#a == $#b;
617              
618 0         0 my @cc=_range2cidr(\@a, \@b);
619              
620 0         0 while ($#cc >= 0)
621             {
622 0         0 $a=shift @cc;
623 0         0 $b=shift @cc;
624              
625 0 0       0 $a=_ipv4to6($a) if $isipv6_1;
626              
627 0         0 push @c, "$a/$b";
628             }
629             }
630 0 0 0     0 return @c unless(1==@r && 1==@c && !wantarray());
      0        
631 0         0 return $c[0];
632             }
633              
634             sub _range2cidr {
635 0     0   0 my $a=shift;
636 0         0 my $b=shift;
637              
638 0         0 my @a=@$a;
639 0         0 my @b=@$b;
640              
641 0         0 $a=shift @a;
642 0         0 $b=shift @b;
643              
644 0 0       0 return _range2cidr8($a, $b) if $#a < 0; # Least significant octet pair.
645              
646 0 0 0     0 croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      0        
647 0 0 0     0 croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
      0        
      0        
648              
649 0         0 my @c;
650              
651 0 0       0 if ($a == $b) # Same start/end octet
652             {
653 0         0 my @cc= _range2cidr(\@a, \@b);
654              
655 0         0 while ($#cc >= 0)
656             {
657 0         0 my $c=shift @cc;
658              
659 0         0 push @c, "$a.$c";
660              
661 0         0 $c=shift @cc;
662 0         0 push @c, $c+8;
663             }
664 0         0 return @c;
665             }
666              
667 0         0 my $start0=1;
668 0         0 my $end255=1;
669              
670 0 0       0 grep { $start0=0 unless $_ == 0; } @a;
  0         0  
671 0 0       0 grep { $end255=0 unless $_ == 255; } @b;
  0         0  
672              
673 0 0       0 if ( ! $start0 )
674             {
675 0         0 my @bcopy=@b;
676              
677 0         0 grep { $_=255 } @bcopy;
  0         0  
678              
679 0         0 my @cc= _range2cidr(\@a, \@bcopy);
680              
681 0         0 while ($#cc >= 0)
682             {
683 0         0 my $c=shift @cc;
684              
685 0         0 push @c, "$a.$c";
686              
687 0         0 $c=shift @cc;
688 0         0 push @c, $c + 8;
689             }
690              
691 0         0 ++$a;
692             }
693              
694 0 0       0 if ( ! $end255 )
695             {
696 0         0 my @acopy=@a;
697              
698 0         0 grep { $_=0 } @acopy;
  0         0  
699              
700 0         0 my @cc= _range2cidr(\@acopy, \@b);
701              
702 0         0 while ($#cc >= 0)
703             {
704 0         0 my $c=shift @cc;
705              
706 0         0 push @c, "$b.$c";
707              
708 0         0 $c=shift @cc;
709 0         0 push @c, $c + 8;
710             }
711              
712 0         0 --$b;
713             }
714              
715 0 0       0 if ($a <= $b)
716             {
717 0         0 grep { $_=0 } @a;
  0         0  
718              
719 0         0 my $pfix=join(".", @a);
720              
721 0         0 my @cc= _range2cidr8($a, $b);
722              
723 0         0 while ($#cc >= 0)
724             {
725 0         0 my $c=shift @cc;
726              
727 0         0 push @c, "$c.$pfix";
728              
729 0         0 $c=shift @cc;
730 0         0 push @c, $c;
731             }
732             }
733 0         0 return @c;
734             }
735              
736             sub _range2cidr8 {
737              
738 0     0   0 my @c;
739              
740 0         0 my @r=@_;
741              
742 0         0 while ($#r >= 0)
743             {
744 0         0 my $a=shift @r;
745 0         0 my $b=shift @r;
746              
747 0 0 0     0 croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      0        
748 0 0 0     0 croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a;
      0        
      0        
749              
750 0         0 ++$b;
751              
752 0         0 while ($a < $b)
753             {
754 0         0 my $i=0;
755 0         0 my $n=1;
756              
757 0         0 while ( ($n & $a) == 0)
758             {
759 0         0 ++$i;
760 0         0 $n <<= 1;
761 0 0       0 last if $i >= 8;
762             }
763              
764 0   0     0 while ($i && $n + $a > $b)
765             {
766 0         0 --$i;
767 0         0 $n >>= 1;
768             }
769              
770 0         0 push @c, $a;
771 0         0 push @c, 8-$i;
772              
773 0         0 $a += $n;
774             }
775             }
776              
777 0         0 return @c;
778             }
779              
780             sub _cidr2range8 {
781              
782 3     3   7 my @c=@_;
783              
784 3         3 my @r;
785              
786 3         5 while ($#c >= 0)
787             {
788 3         5 my $a=shift @c;
789 3         13 my $b=shift @c;
790              
791 3 50 33     20 croak "Bad starting address" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/;
      33        
792 3 50 33     19 croak "Bad ending address" unless $b >= 0 && $b <= 8 && $b =~ /^[0-9]+$/;
      33        
793              
794 3         6 my $n= 1 << (8-$b);
795              
796 3         7 $a &= ($n-1) ^ 255;
797              
798 3         5 push @r, $a;
799 3         7 push @r, $a + ($n-1);
800             }
801 3         7 return @r;
802             }
803              
804             sub _ipcmp {
805 24     24   39 my $aa=shift;
806 24         32 my $bb=shift;
807              
808 24         33 my $isipv6_1;
809             my $isipv6_2;
810              
811 24         47 ($isipv6_1, $aa)=_ipv6to4($aa);
812 24         44 ($isipv6_2, $bb)=_ipv6to4($bb);
813              
814 24         75 my @a=split (/\./, $aa);
815 24         84 my @b=split (/\./, $bb);
816              
817 24 100       66 unshift @a, (0,0,0,0,0,0,0,0,0,0,255,255)
818             unless $isipv6_1;
819              
820 24 100       56 unshift @b, (0,0,0,0,0,0,0,0,0,0,255,255)
821             unless $isipv6_2;
822              
823 24 50       105 croak "Different number of octets in IP addresses" unless $#a == $#b;
824              
825 24   66     101 while ($#a >= 0 && $a[0] == $b[0])
826             {
827 163         192 shift @a;
828 163         463 shift @b;
829             }
830              
831 24 50       47 return 0 if $#a < 0;
832              
833 24         109 return $a[0] <=> $b[0];
834             }
835              
836              
837             =pod
838              
839             =head2 @octet_list=Net::CIDR::cidr2octets(@cidr_list);
840              
841             cidr2octets() takes @cidr_list and returns a list of leading octets
842             representing those netblocks. Example:
843              
844             @octet_list=Net::CIDR::cidr2octets("10.0.0.0/14", "192.168.0.0/24");
845              
846             The result is the following five-element array:
847             ("10.0", "10.1", "10.2", "10.3", "192.168.0").
848              
849             For IPv6 addresses, the hexadecimal words in the resulting list are
850             zero-padded:
851              
852             @octet_list=Net::CIDR::cidr2octets("::dead:beef:0:0/110");
853              
854             The result is a four-element array:
855             ("0000:0000:0000:0000:dead:beef:0000",
856             "0000:0000:0000:0000:dead:beef:0001",
857             "0000:0000:0000:0000:dead:beef:0002",
858             "0000:0000:0000:0000:dead:beef:0003").
859             Prefixes of IPv6 CIDR blocks should be even multiples of 16 bits, otherwise
860             they can potentially expand out to a 32,768-element array, each!
861              
862             =cut
863              
864             sub cidr2octets {
865 2     2 1 1491 my @cidr=@_;
866              
867 2         2 my @r;
868              
869 2         5 while ($#cidr >= 0)
870             {
871 3         3 my $cidr=shift @cidr;
872              
873 3         6 $cidr =~ s/\s//g;
874              
875 3 50       10 croak "CIDR \"$cidr\" doesn't look like a CIDR\n" unless ($cidr =~ /(.*)\/(.*)/);
876              
877 3         7 my ($ip, $pfix)=($1, $2);
878              
879 3         3 my $isipv6;
880              
881 3         5 my @ips=_iptoipa($ip);
882              
883 3         4 $isipv6=shift @ips;
884              
885 3 50 33     35 croak "$pfix, as in '$cidr', does not make sense"
      33        
886             unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/;
887              
888 3         4 my $i;
889              
890 3         8 for ($i=0; $i <= $#ips; $i++)
891             {
892 20 100       29 last if $pfix - $i * 8 < 8;
893             }
894              
895 3         5 my @msb=splice @ips, 0, $i;
896              
897 3         3 my $bitsleft= $pfix - $i * 8;
898              
899 3 100 66     9 if ($#ips < 0 || $bitsleft == 0)
900             {
901 1 50 33     10 if ($pfix == 0 && $bitsleft == 0)
    50          
902             {
903 0         0 foreach (0..255)
904             {
905 0         0 my @n=($_);
906              
907 0 0       0 if ($isipv6)
908             {
909 0         0 _push_ipv6_octets(\@r, \@n);
910             }
911             else
912             {
913 0         0 push @r, $n[0];
914             }
915             }
916             }
917             elsif ($isipv6)
918             {
919 0         0 _push_ipv6_octets(\@r, \@msb);
920             }
921             else
922             {
923 1         3 push @r, join(".", @msb);
924             }
925 1         2 next;
926             }
927              
928 2         3 my @rr=_cidr2range8(($ips[0], $bitsleft));
929              
930 2         6 while ($#rr >= 0)
931             {
932 2         2 my $a=shift @rr;
933 2         2 my $b=shift @rr;
934              
935             grep {
936 2 100       3 if ($isipv6)
  8         12  
937             {
938 4         5 push @msb, $_;
939 4         6 _push_ipv6_octets(\@r, \@msb);
940 4         10 pop @msb;
941             }
942             else
943             {
944 4         10 push @r, join(".", (@msb, $_));
945             }
946             } ($a .. $b);
947             }
948             }
949              
950 2         8 return @r;
951             }
952              
953             sub _push_ipv6_octets {
954 4     4   6 my $ary_ref=shift;
955 4         4 my $octets=shift;
956              
957 4 50       3 if ( ($#{$octets} % 2) == 0) # Odd number of octets
  4         7  
958             {
959 0         0 foreach (0 .. 255)
960             {
961 0         0 push @$octets, $_;
962 0         0 _push_ipv6_octets($ary_ref, $octets);
963 0         0 pop @$octets;
964             }
965 0         0 return;
966             }
967              
968 4         5 my $i;
969 4         4 my $s="";
970              
971 4         5 for ($i=0; $i <= $#{$octets}; $i += 2)
  32         69  
972             {
973 28 100       36 $s .= ":" if $s ne "";
974 28         44 $s .= sprintf("%02x%02x", $$octets[$i], $$octets[$i+1]);
975             }
976 4         7 push @$ary_ref, $s;
977             }
978              
979             =pod
980              
981             =head2 @cidr_list=Net::CIDR::cidradd($block, @cidr_list);
982              
983             The cidradd() functions allows a CIDR list to be built one CIDR netblock
984             at a time, merging adjacent and overlapping ranges.
985             $block is a single netblock, expressed as either "start-finish", or
986             "address/prefix".
987             Example:
988              
989             @cidr_list=Net::CIDR::range2cidr("192.168.0.0-192.168.0.255");
990             @cidr_list=Net::CIDR::cidradd("10.0.0.0/8", @cidr_list);
991             @cidr_list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @cidr_list);
992              
993             The result is a two-element array: ("10.0.0.0/8", "192.168.0.0/23").
994             IPv6 addresses are handled in an analogous fashion.
995              
996             =cut
997              
998             sub cidradd {
999 0     0 1 0 my @cidr=@_;
1000              
1001 0         0 my $ip=shift @cidr;
1002              
1003 0 0       0 $ip="$ip-$ip" unless $ip =~ /[-\/]/;
1004              
1005 0         0 unshift @cidr, $ip;
1006              
1007 0         0 @cidr=cidr2range(@cidr);
1008              
1009 0         0 my @a;
1010             my @b;
1011              
1012             grep {
1013 0 0       0 croak "Range $_ doesn't look like start-end\n" unless /(.*)-(.*)/;
  0         0  
1014 0         0 push @a, $1;
1015 0         0 push @b, $2;
1016             } @cidr;
1017              
1018 0         0 my $lo=shift @a;
1019 0         0 my $hi=shift @b;
1020              
1021 0         0 my $i;
1022              
1023 0         0 for ($i=0; $i <= $#a; $i++)
1024             {
1025 0 0       0 last if _ipcmp($lo, $hi) > 0;
1026              
1027 0 0       0 next if _ipcmp($b[$i], $lo) < 0;
1028 0 0       0 next if _ipcmp($hi, $a[$i]) < 0;
1029              
1030 0 0 0     0 if (_ipcmp($a[$i],$lo) <= 0 && _ipcmp($hi, $b[$i]) <= 0)
1031             {
1032 0         0 $lo=_add1($hi);
1033 0         0 last;
1034             }
1035              
1036 0 0       0 if (_ipcmp($a[$i],$lo) <= 0)
1037             {
1038 0         0 $lo=_add1($b[$i]);
1039 0         0 next;
1040             }
1041              
1042 0 0       0 if (_ipcmp($hi, $b[$i]) <= 0)
1043             {
1044 0         0 $hi=_sub1($a[$i]);
1045 0         0 next;
1046             }
1047              
1048 0         0 $a[$i]=undef;
1049 0         0 $b[$i]=undef;
1050             }
1051              
1052 0 0 0     0 unless ((! defined $lo) || (! defined $hi) || _ipcmp($lo, $hi) > 0)
      0        
1053             {
1054 0         0 push @a, $lo;
1055 0         0 push @b, $hi;
1056             }
1057              
1058 0         0 @cidr=();
1059              
1060 0         0 @a=grep ( (defined $_), @a);
1061 0         0 @b=grep ( (defined $_), @b);
1062              
1063 0         0 for ($i=0; $i <= $#a; $i++)
1064             {
1065 0         0 push @cidr, "$a[$i]-$b[$i]";
1066             }
1067              
1068             @cidr=sort {
1069 0         0 $a =~ /(.*)-/;
  0         0  
1070              
1071 0         0 my $c=$1;
1072              
1073 0         0 $b =~ /(.*)-/;
1074              
1075 0         0 my $d=$1;
1076              
1077 0         0 my $e=_ipcmp($c, $d);
1078 0         0 return $e;
1079             } @cidr;
1080              
1081 0         0 $i=0;
1082              
1083 0         0 while ($i < $#cidr)
1084             {
1085 0         0 $cidr[$i] =~ /(.*)-(.*)/;
1086              
1087 0         0 my ($k, $l)=($1, $2);
1088              
1089 0         0 $cidr[$i+1] =~ /(.*)-(.*)/;
1090              
1091 0         0 my ($m, $n)=($1, $2);
1092              
1093 0 0       0 if (_ipcmp( _add1($l), $m) == 0)
1094             {
1095 0         0 splice @cidr, $i, 2, "$k-$n";
1096 0         0 next;
1097             }
1098 0         0 ++$i;
1099             }
1100              
1101 0         0 return range2cidr(@cidr);
1102             }
1103              
1104              
1105             sub _add1 {
1106 0     0   0 my $n=shift;
1107              
1108 0         0 my $isipv6;
1109              
1110 0         0 ($isipv6, $n)=_ipv6to4($n);
1111              
1112 0         0 my @ip=split(/\./, $n);
1113              
1114 0         0 my $i=$#ip;
1115              
1116 0         0 while ($i >= 0)
1117             {
1118 0 0       0 last if ++$ip[$i] < 256;
1119 0         0 $ip[$i]=0;
1120 0         0 --$i;
1121             }
1122              
1123 0 0       0 return undef if $i < 0;
1124              
1125 0         0 $i=join(".", @ip);
1126 0 0       0 $i=_ipv4to6($i) if $isipv6;
1127 0         0 return $i;
1128              
1129             }
1130              
1131             sub _sub1 {
1132 0     0   0 my $n=shift;
1133              
1134 0         0 my $isipv6;
1135              
1136 0         0 ($isipv6, $n)=_ipv6to4($n);
1137              
1138 0         0 my @ip=split(/\./, $n);
1139              
1140 0         0 my $i=$#ip;
1141              
1142 0         0 while ($i >= 0)
1143             {
1144 0 0       0 last if --$ip[$i] >= 0;
1145 0         0 $ip[$i]=255;
1146 0         0 --$i;
1147             }
1148              
1149 0 0       0 return undef if $i < 0;
1150              
1151 0         0 $i=join(".", @ip);
1152 0 0       0 $i=_ipv4to6($i) if $isipv6;
1153 0         0 return $i;
1154             }
1155              
1156             =pod
1157              
1158             =head2 $found=Net::CIDR::cidrlookup($ip, @cidr_list);
1159              
1160             Search for $ip in @cidr_list. $ip can be a single IP address, or a
1161             netblock in CIDR or start-finish notation.
1162             lookup() returns 1 if $ip overlaps any netblock in @cidr_list, 0 if not.
1163              
1164             =cut
1165              
1166             sub cidrlookup {
1167 10     10 1 17443 my @cidr=@_;
1168              
1169 10         20 my $ip=shift @cidr;
1170              
1171 10 50       89 $ip="$ip-$ip" unless $ip =~ /[-\/]/;
1172              
1173 10         19 unshift @cidr, $ip;
1174              
1175 10         26 @cidr=cidr2range(@cidr);
1176              
1177 10         19 my @a;
1178             my @b;
1179              
1180             grep {
1181 10 50       18 croak "Range $_ doesn't look like start-end\n" unless /(.*)-(.*)/;
  30         105  
1182 30         76 push @a, $1;
1183 30         86 push @b, $2;
1184             } @cidr;
1185              
1186 10         16 my $lo=shift @a;
1187 10         15 my $hi=shift @b;
1188              
1189 10         15 my $i;
1190              
1191 10         23 for ($i=0; $i <= $#a; $i++)
1192             {
1193 17 100       45 next if _ipcmp($b[$i], $lo) < 0;
1194 7 100       15 next if _ipcmp($hi, $a[$i]) < 0;
1195 4         60 return 1;
1196             }
1197              
1198 6         54 return 0;
1199             }
1200              
1201             =pod
1202              
1203             =head2 $ip=Net::CIDR::cidrvalidate($ip);
1204              
1205             Validate whether $ip is a valid IPv4 or IPv6 address, or a CIDR.
1206             Returns its validated argument or undef.
1207             Spaces are removed, and IPv6 hexadecimal address are converted to lowercase.
1208              
1209             $ip with less than four octets gets filled out with additional octets, and
1210             the modified value gets returned. This turns "192.168/16" into a proper
1211             "192.168.0.0/16".
1212              
1213             If $ip contains a "/", it must be a valid CIDR, otherwise it must be a valid
1214             IPv4 or an IPv6 address.
1215              
1216             A technically invalid CIDR, such as "192.168.0.1/24" fails validation, returning
1217             undef.
1218              
1219             =cut
1220              
1221             sub _compress_ipv6 {
1222             # taken from IPv6::Address on CPAN
1223 11     11   24 my $str = shift;
1224 11 50       28 return '::' if($str eq '0:0:0:0:0:0:0:0');
1225              
1226             # _ipv4to6, called from addr(), finds the longest sequence of
1227             # consecutive 0s and replaces them with a ::, and only the first
1228             # instance (if there were two or more sequences of the same length),
1229             # we're carefully reproducing those semantics here.
1230 11         41 for(my $i=7;$i>1;$i--) {
1231 64         228 my $zerostr = join(':',split('','0'x$i));
1232             ###print "DEBUG: $str $zerostr \n";
1233 64 100       1771 if ($str =~ /^$zerostr:/) {
    100          
    50          
1234 1         5 $str =~ s/^$zerostr:/::/;
1235 1         3 return $str;
1236             }
1237             elsif ($str =~ /:$zerostr:/) {
1238 1         10 $str =~ s/:$zerostr:/::/;
1239 1         7 return $str;
1240             }
1241             elsif($str =~ /:$zerostr$/) {
1242 0         0 $str =~ s/:$zerostr$/::/;
1243 0         0 return $str;
1244             }
1245             }
1246 9         36 return $str;
1247             }
1248              
1249             sub cidrvalidate {
1250 17     17 1 26611 my $v=shift;
1251              
1252 17         65 $v =~ s/\s//g;
1253              
1254 17         49 $v=lc($v);
1255              
1256 17         30 my $suffix;
1257              
1258 17 100       124 ($v, $suffix)=($1, $2) if $v =~ m@(.*)/(.*)@;
1259              
1260 17 100       52 if (defined $suffix)
1261             {
1262 8 50 33     91 return undef unless $suffix =~ /^\d+$/ &&
      33        
1263             ($suffix eq "0" || $suffix =~ /^[123456789]/);
1264             }
1265              
1266 17 100 100     175 if ($v =~ /^([0-9\.]+)$/ || $v =~ /^::ffff:([0-9\.]+)$/ ||
      66        
1267             $v =~ /^:([0-9\.]+)$/)
1268             {
1269 6         17 my $n=$1;
1270              
1271 6 50 33     74 return undef if $n =~ /^\./ || $n =~ /\.$/ || $n =~ /\.\./;
      33        
1272              
1273 6         25 my @o= split(/\./, $n);
1274              
1275 6         19 while ($#o < 3)
1276             {
1277 0         0 push @o, "0";
1278             }
1279              
1280 6         21 $n=join(".", @o);
1281              
1282 6 50       18 return undef if $#o != 3;
1283              
1284 6         16 foreach (@o)
1285             {
1286 24 50       55 return undef if /^0./;
1287 24 50 33     91 return undef if $_ < 0 || $_ > 255;
1288             }
1289              
1290 6 100       23 if ($v =~ /^::ffff/)
1291             {
1292 3 100       11 $suffix=128 unless defined $suffix;
1293              
1294 3 50       10 return undef if $suffix < 128-32;
1295              
1296 3         7 $suffix -= 128-32;
1297             }
1298             else
1299             {
1300 3 100       8 $suffix=32 unless defined $suffix;
1301             }
1302              
1303 6         43 foreach (addr2cidr($n))
1304             {
1305 86 100       250 return $_ if $_ eq "$n/$suffix";
1306             }
1307 2         24 return undef;
1308             }
1309              
1310 11 50       64 return undef unless $v =~ /^[0-9a-f:]+$/;
1311              
1312 11 50 33     138 return undef if $v =~ /:::/ || $v =~ /^:[^:]/ || $v =~ /[^:]:$/
      33        
      33        
1313             || $v =~ /::.*::/;
1314              
1315 11         133 my @o=grep (/./, split(/:/, $v));
1316              
1317 11 50 66     84 return undef if ($#o >= 8 || ($#o<7 && $v !~ /::/));
      33        
1318              
1319 11         29 foreach (@o)
1320             {
1321 55 50       119 return undef if length ($_) > 4;
1322             }
1323              
1324 11 100       32 $suffix=128 unless defined $suffix;
1325              
1326 11         77 $v =~ s/([0-9A-Fa-f]+)/_triml0($1)/ge;
  55         108  
1327              
1328 11         48 my @compressed = _compress_ipv6($v);
1329              
1330 11 100       50 unless ($compressed[0] =~ /::/)
1331             {
1332 3         16 my @split = split(/:/, $compressed[0]);
1333              
1334 3         29 foreach my $i (0..$#split)
1335             {
1336 17 100       46 if ($split[$i] eq "0")
1337             {
1338 2         11 my @cpy = @split;
1339              
1340 2         3 $cpy[$i] = "";
1341              
1342 2         9 push @compressed, join(":", @cpy);
1343 2         9 last;
1344             }
1345             }
1346             }
1347              
1348 11         39 foreach my $candidate (addr2cidr($v))
1349             {
1350 327         480 foreach my $c (@compressed)
1351             {
1352 329 100       897 return $candidate if $candidate eq "$c/$suffix";
1353             }
1354             }
1355 1         18 return undef;
1356             }
1357              
1358             sub _triml0 {
1359 55     55   145 my ($a) = @_;
1360              
1361 55         112 $a =~ s/^0+//g;
1362 55 100       119 $a = "0" if $a eq '';
1363 55         181 return $a
1364             }
1365              
1366             =pod
1367              
1368             =head1 BUGS
1369              
1370             Garbage in, garbage out.
1371             Always use cidrvalidate() before doing anything with untrusted input.
1372             Otherwise,
1373             "slightly" invalid input will work (extraneous whitespace
1374             is generally OK),
1375             but the functions will croak if you're totally off the wall.
1376              
1377             =head1 AUTHOR
1378              
1379             Sam Varshavchik
1380              
1381             With some contributions from David Cantrell
1382             and brian d foy .
1383              
1384             =cut
1385              
1386             __END__