File Coverage

blib/lib/Paranoid/Network/IPv4.pm
Criterion Covered Total %
statement 111 113 98.2
branch 28 34 82.3
condition 13 21 61.9
subroutine 21 21 100.0
pod 5 5 100.0
total 178 194 91.7


line stmt bran cond sub pod time code
1             # Paranoid::Network::IPv4 -- IPv4-specific network functions
2             #
3             # $Id: lib/Paranoid/Network/IPv4.pm, 2.08 2020/12/31 12:10:06 acorliss Exp $
4             #
5             # This software is free software. Similar to Perl, you can redistribute it
6             # and/or modify it under the terms of either:
7             #
8             # a) the GNU General Public License
9             # as published by the
10             # Free Software Foundation ; either version 1
11             # , or any later version
12             # , or
13             # b) the Artistic License 2.0
14             # ,
15             #
16             # subject to the following additional term: No trademark rights to
17             # "Paranoid" have been or are conveyed under any of the above licenses.
18             # However, "Paranoid" may be used fairly to describe this unmodified
19             # software, in good faith, but not as a trademark.
20             #
21             # (c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com)
22             # (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)
23             #
24             #####################################################################
25              
26             #####################################################################
27             #
28             # Environment definitions
29             #
30             #####################################################################
31              
32             package Paranoid::Network::IPv4;
33              
34 3     3   622 use 5.008;
  3         17  
35              
36 3     3   16 use strict;
  3         4  
  3         58  
37 3     3   37 use warnings;
  3         7  
  3         106  
38 3     3   18 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  3         5  
  3         187  
39 3     3   18 use base qw(Exporter);
  3         14  
  3         216  
40 3     3   18 use Paranoid;
  3         7  
  3         170  
41 3     3   20 use Paranoid::Debug qw(:all);
  3         6  
  3         612  
42 3     3   511 use Paranoid::Network::Socket;
  3         6  
  3         1850  
43              
44             my @base = qw(ipv4NetConvert ipv4NetIntersect);
45             my @constants = qw(MAXIPV4CIDR IPV4REGEX IPV4CIDRRGX IPV4BASE IPV4BRDCST
46             IPV4MASK);
47             my @ipv4sort = qw(ipv4NumSort ipv4StrSort ipv4PackedSort);
48              
49             ($VERSION) = ( q$Revision: 2.08 $ =~ /(\d+(?:\.\d+)+)/sm );
50             @EXPORT = @base;
51             @EXPORT_OK = ( @base, @constants, @ipv4sort );
52             %EXPORT_TAGS = (
53             all => [@EXPORT_OK],
54             base => [@base],
55             constants => [@constants],
56             ipv4Sort => [@ipv4sort],
57             );
58              
59 3     3   25 use constant MAXIPV4CIDR => 32;
  3         5  
  3         403  
60 3         276 use constant IPV4REGEX =>
61 3     3   22 qr/(?:(?:25[0-5]|2[0-4][0-9]|1?\d\d?)\.){3}(?:25[0-5]|2[0-4][0-9]|1?\d\d?)/s;
  3         6  
62 3         6 use constant IPV4CIDRRGX =>
63 3     3   23 qr#@{[ IPV4REGEX ]}/(?:(?:3[0-2]|[12]?\d)|@{[ IPV4REGEX ]})#s;
  3         4  
  3         8  
  3         487  
64 3     3   21 use constant FULLMASK => 0xffffffff;
  3         12  
  3         129  
65 3     3   51 use constant IPV4BASE => 0;
  3         8  
  3         155  
66 3     3   19 use constant IPV4BRDCST => 1;
  3         5  
  3         131  
67 3     3   17 use constant IPV4MASK => 2;
  3         8  
  3         2169  
68              
69             #####################################################################
70             #
71             # Module code follows
72             #
73             #####################################################################
74              
75             sub ipv4NetConvert {
76              
77             # Purpose: Takes a string representation of an IPv4 network
78             # address and returns a list containing the binary
79             # network address, broadcast address, and netmask.
80             # Also allows for a plain IP being passed, in which
81             # case it only returns the binary IP.
82             # Returns: Array, empty on errors
83             # Usage: @network = ipv4NetConvert($netAddr);
84              
85 41     41 1 9106 my $netAddr = shift;
86 41         65 my ( $bnet, $bmask, $t, @rv );
87              
88 41         110 pdebug( 'entering w/%s', PDLEVEL1, $netAddr );
89 41         99 pIn();
90              
91             # Extract net address, mask
92 41 100       77 if ( defined $netAddr ) {
93 40         62 ($t) = ( $netAddr =~ m#^(@{[ IPV4CIDRRGX ]}|@{[ IPV4REGEX ]})$#s )[0];
  40         70  
  40         591  
94 40 100       179 ( $bnet, $bmask ) = split m#/#s, $t if defined $t;
95             }
96              
97 41 100 66     147 if ( defined $bnet and length $bnet ) {
98              
99             # First, convert $bnet to see if we have a valid IP address
100 33         171 $bnet = unpack 'N', inet_aton($bnet);
101              
102 33 50 33     121 if ( defined $bnet and length $bnet ) {
103              
104             # Save our network address
105 33         64 push @rv, $bnet;
106              
107 33 100 66     99 if ( defined $bmask and length $bmask ) {
108              
109             # Convert netmask
110 24 50       140 $bmask =
    100          
111             $bmask !~ /^\d+$/s ? unpack 'N', inet_aton($bmask)
112             : $bmask <= MAXIPV4CIDR
113             ? FULLMASK - ( ( 2**( MAXIPV4CIDR - $bmask ) ) - 1 )
114             : undef;
115              
116 24 50 33     99 if ( defined $bmask and length $bmask ) {
117              
118             # Apply the mask to the base address
119 24         45 $rv[IPV4BASE] = $rv[IPV4BASE] & $bmask;
120              
121             # Calculate and save our broadcast address
122 24         42 push @rv, $bnet | ( $bmask ^ FULLMASK );
123              
124             # Save our mask
125 24         36 push @rv, $bmask;
126              
127             } else {
128 0         0 pdebug( 'invalid netmask passed', PDLEVEL1 );
129             }
130             }
131             } else {
132 0         0 pdebug( 'failed to convert IPv4 address', PDLEVEL1 );
133             }
134             } else {
135 8         23 pdebug( 'failed to extract an IPv4 address', PDLEVEL1 );
136             }
137              
138 41         115 pOut();
139 41         100 pdebug( 'leaving w/rv: %s', PDLEVEL1, @rv );
140              
141 41         119 return @rv;
142             }
143              
144             sub ipv4NetIntersect {
145              
146             # Purpose: Tests whether network address ranges intersect
147             # Returns: Integer, denoting whether an intersection exists, and what
148             # kind:
149             #
150             # -1: destination range encompasses target range
151             # 0: both ranges do not intersect at all
152             # 1: target range encompasses destination range
153             #
154             # Usage: $rv = ipv4NetIntersect($net1, $net2);
155              
156 18     18 1 677 my $tgt = shift;
157 18         27 my $dest = shift;
158 18         29 my $rv = 0;
159 18         31 my ( @tnet, @dnet );
160              
161 18         50 pdebug( 'entering w/%s, %s', PDLEVEL1, $tgt, $dest );
162 18         44 pIn();
163              
164             # Bypas if one or both isn't defined -- obviously no intersection
165 18 50 33     72 unless ( !defined $tgt or !defined $dest ) {
166              
167             # Convert addresses (also allows for raw IPs (32bit integers) to be
168             # passed)
169 18 50       81 @tnet = $tgt =~ /^\d+$/s ? ($tgt) : ipv4NetConvert($tgt);
170 18 50       68 @dnet = $dest =~ /^\d+$/s ? ($dest) : ipv4NetConvert($dest);
171              
172             # insert bogus numbers for non IP-address info
173 18 100       44 @tnet = (-1) unless scalar @tnet;
174 18 100       47 @dnet = (-2) unless scalar @dnet;
175              
176             # Dummy up broadcast address for those single IPs passed (in lieu of
177             # network ranges)
178 18 100       44 $tnet[IPV4BRDCST] = $tnet[IPV4BASE] if $#tnet == 0;
179 18 100       39 $dnet[IPV4BRDCST] = $dnet[IPV4BASE] if $#dnet == 0;
180              
181 18 100 100     118 if ( $tnet[IPV4BASE] <= $dnet[IPV4BASE]
    100 100        
182             and $tnet[IPV4BRDCST] >= $dnet[IPV4BRDCST] ) {
183              
184             # Target fully encapsulates dest
185 4         9 $rv = 1;
186              
187             } elsif ( $tnet[IPV4BASE] >= $dnet[IPV4BASE]
188             and $tnet[IPV4BRDCST] <= $dnet[IPV4BRDCST] ) {
189              
190             # Dest fully encapsulates target
191 7         12 $rv = -1;
192              
193             }
194             }
195              
196 18         47 pOut();
197 18         49 pdebug( 'leaving w/rv: %s', PDLEVEL1, $rv );
198              
199 18         81 return $rv;
200             }
201              
202             {
203              
204 3     3   26 no strict 'refs';
  3         5  
  3         977  
205              
206             sub ipv4NumSort {
207              
208             # Purpose: Sorts IPv4 addresses represented in numeric form
209             # Returns: -1, 0, 1
210             # Usage: @sorted = sort &ipv4NumSort @ipv4;
211              
212 2     2 1 1981 my ($pkg) = caller;
213              
214 2         5 return ${"${pkg}::a"} <=> ${"${pkg}::b"};
  2         5  
  2         9  
215             }
216              
217             sub ipv4PackedSort {
218              
219             # Purpose: Sorts IPv4 addresses represented in packed strings
220             # Returns: -1, 0, 1
221             # Usage: @sorted = sort &ipv4PackedSort @ipv4;
222              
223 2     2 1 1985 my ($pkg) = caller;
224              
225 2         5 my $a1 = unpack 'N', ${"${pkg}::a"};
  2         8  
226 2         3 my $b1 = unpack 'N', ${"${pkg}::b"};
  2         6  
227              
228 2         24 return $a1 <=> $b1;
229             }
230              
231             sub ipv4StrSort {
232              
233             # Purpose: Sorts IPv4 addresses represented in string form
234             # Returns: -1, 0, 1
235             # Usage: @sorted = sort &ipv4StrSort @ipv4;
236              
237 2     2 1 6 my ($pkg) = caller;
238              
239 2         5 my $a1 = ${"${pkg}::a"};
  2         6  
240 2         10 my $b1 = ${"${pkg}::b"};
  2         6  
241              
242 2         5 $a1 =~ s#/.+##s;
243 2         10 $a1 = unpack 'N', inet_aton($a1);
244 2         8 $b1 =~ s#/.+##s;
245 2         6 $b1 = unpack 'N', inet_aton($b1);
246              
247 2         9 return $a1 <=> $b1;
248             }
249             }
250              
251             1;
252              
253             __END__