File Coverage

blib/lib/Net/Subnet.pm
Criterion Covered Total %
statement 53 55 96.3
branch 17 22 77.2
condition 6 8 75.0
subroutine 18 18 100.0
pod 3 8 37.5
total 97 111 87.3


line stmt bran cond sub pod time code
1             package Net::Subnet;
2              
3 2     2   27604 use strict;
  2         5  
  2         74  
4 2     2   2083 use Socket;
  2         9091  
  2         1477  
5             BEGIN {
6 2 50   2   27 if (defined &Socket::inet_pton) {
7 2         422 Socket->import(qw(inet_pton AF_INET6));
8             } else {
9 0         0 require Socket6;
10 0         0 Socket6->import(qw(inet_pton AF_INET6));
11             }
12             };
13              
14              
15 2     2   17 use base 'Exporter';
  2         3  
  2         1314  
16             our @EXPORT = qw(subnet_matcher subnet_classifier sort_subnets);
17              
18             our $VERSION = '1.03';
19              
20             sub cidr2mask_v4 {
21 3     3 0 5 my ($length) = @_;
22 3         11 return pack "N", 0xffffffff << (32 - $length);
23             }
24              
25             sub cidr2mask_v6 {
26 12     12 0 13 my ($length) = @_;
27 12         36 return pack('B128', '1' x $length);
28             }
29              
30             sub subnet_matcher {
31 11 100   11 1 448 @_ > 1 and goto &multi_matcher;
32              
33 10         26 my ($net, $mask) = split m[/], shift;
34 10 100       29 return $net =~ /:/
35             ? ipv6_matcher($net, $mask)
36             : ipv4_matcher($net, $mask);
37             }
38              
39             sub ipv4_matcher {
40 2     2 0 3 my ($net, $mask) = @_;
41              
42 2         7 $net = inet_aton($net);
43 2 50       7 $mask = $mask =~ /\./ ? inet_aton($mask) : cidr2mask_v4($mask);
44              
45 2         3 my $masked_net = $net & $mask;
46              
47 2   50 14   18 return sub { ((inet_aton(shift) // return !1) & $mask) eq $masked_net };
  14         129  
48             }
49              
50             sub ipv6_matcher {
51 8     8 0 9 my ($net, $mask) = @_;
52              
53 8         19 $net = inet_pton(AF_INET6, $net);
54 8 50       17 $mask = $mask =~ /:/ ? inet_pton(AF_INET6, $mask) : cidr2mask_v6($mask);
55              
56 8         12 my $masked_net = $net & $mask;
57              
58 75   50 75   486 return sub { ((inet_pton(AF_INET6,shift)//return!1) & $mask) eq $masked_net}
59 8         52 }
60              
61             sub multi_matcher {
62 1     1 0 7 my @v4 = map subnet_matcher($_), grep !/:/, @_;
63 1         5 my @v6 = map subnet_matcher($_), grep /:/, @_;
64              
65             return sub {
66 19 100 100 19   5266 $_->($_[0]) and return 1 for $_[0] =~ /:/ ? @v6 : @v4;
67 9         32 return !!0;
68             }
69 1         5 }
70              
71 2     2   22 use constant MATCHER => 0;
  2         3  
  2         145  
72 2     2   9 use constant SUBNET => 1;
  2         2  
  2         887  
73              
74             sub subnet_classifier {
75             # MATCHER, SUBNET
76 1     1 1 13 my @v4 = map [ subnet_matcher($_), $_ ], grep !/:/, @_;
77 1         6 my @v6 = map [ subnet_matcher($_), $_ ], grep /:/, @_;
78              
79             return sub {
80             $_->[MATCHER]->($_[0]) and return $_->[SUBNET]
81 19 100 100 19   78 for $_[0] =~ /:/ ? @v6 : @v4;
82 9         35 return undef;
83             }
84 1         4 }
85              
86             sub sort_subnets {
87 1     1 1 33 my @unsorted;
88 1         3 for (@_) {
89 5         15 my ($net, $mask) = split m[/];
90              
91 5 50       27 $mask = $net =~ /:/
    50          
    100          
92             ? ($mask =~ /:/ ? inet_pton(AF_INET6, $mask) : cidr2mask_v6($mask))
93             : ($mask =~ /\./ ? inet_aton($mask) : cidr2mask_v4($mask));
94              
95 5 100       41 $net = $net =~ /:/
96             ? inet_pton(AF_INET6, $net)
97             : inet_aton($net);
98              
99 5         22 push @unsorted, sprintf "%-16s%-16s%s", ($net & $mask), $mask, $_;
100             }
101              
102 1         10 return map substr($_, 32), reverse sort @unsorted;
103             }
104              
105             1;
106              
107             __END__